/*
 * UserNet  --  TCP/IP implementation in userspace
 * Copyright (C) 2010  Robert Millan
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <errno.h>
#include <error.h>
#include <fcntl.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>

#include <net/if.h>
#include <net/ethernet.h>	/* ETHER_MAX_LEN */
#include <sys/ioctl.h>
#include <linux/if_tun.h>

struct dev
{
  char name[IFNAMSIZ];
  int fd;
};

static void
new_tap (struct dev *dev)
{
  int fd;

  fd = open ("/dev/net/tun", O_RDWR);
  if (fd == -1)
    error (1, errno, "open");

  struct ifreq ifr;
  memset (&ifr, 0, sizeof (ifr));

  ifr.ifr_flags = IFF_TAP | IFF_NO_PI;

  if (ioctl (fd, TUNSETIFF, (void *) &ifr) < 0)
    error (1, errno, "ioctl");

  printf ("creating %s\n", ifr.ifr_name);

  strncpy (dev->name, ifr.ifr_name, sizeof (dev->name));
  dev->fd = fd;
}

struct ta
{
  struct dev *in;
  struct dev *out;
};

static void *
copier (void *arg)
{
  uint8_t frame[ETHER_MAX_LEN];
  struct ta *a = arg;

  while (1)
    {
      size_t len = read (a->in->fd, frame, sizeof (frame));
      if (len <= 0)
	continue;
      
      write (a->out->fd, frame, len);

      printf ("%ld-byte frame from %s to %s\n", len, a->in->name, a->out->name);
    }

  return NULL;
}

int
main (int argc, char *argv[])
{
  struct dev dev[2];
  pthread_t thread1, thread2;

  new_tap (&dev[0]);
  new_tap (&dev[1]);

  struct ta a1;
  struct ta a2;

  a1.in = &dev[0];
  a1.out = &dev[1];

  a2.in = &dev[1];
  a2.out = &dev[0];

  pthread_create (&thread1, NULL, copier, &a1);
  pthread_create (&thread2, NULL, copier, &a2);

  printf ("ready!\n");

  while (1)
    sleep (10);

  return 0;
}
